//
//  NSString+ZXAvoidCrash.m
//  ZXAvoidCrash
//
//  Created by 朱新明 on 2023/3/27.
//

#import "NSString+ZXAvoidCrash.h"
#import "ZXAvoidCrashRuntime.h"
#import "ZXAvoidCrashReport.h"

@implementation NSString (ZXAvoidCrash)

+ (void)zx_avoidCrashExchangeMethod{
    
    Class NSPlaceholderString = NSClassFromString(@"NSPlaceholderString");
        
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:NSPlaceholderString originalSelector:@selector(initWithUTF8String:) swizzledSelector:@selector(zx_initWithUTF8String:)];
    
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:NSPlaceholderString originalSelector:@selector(initWithString:) swizzledSelector:@selector(zx_initWithString:)];

    Class __NSCFConstantString = NSClassFromString(@"__NSCFConstantString");
    //指针标签在交换方法后，系统方法会直接崩溃，不要用
//    Class NSTaggedPointerString = NSClassFromString(@"NSTaggedPointerString");
    Class __NSCFString = NSClassFromString(@"__NSCFString");
    
    ///characterAtIndex:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(characterAtIndex:) swizzledSelector:@selector(zx_characterAtIndex:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(characterAtIndex:) swizzledSelector:@selector(zx_characterAtIndex:)];

    ///substringFromIndex:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(substringFromIndex:) swizzledSelector:@selector(zx_substringFromIndex:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(substringFromIndex:) swizzledSelector:@selector(zx_substringFromIndex:)];

    ///substringToIndex:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(substringToIndex:) swizzledSelector:@selector(zx_substringToIndex:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(substringToIndex:) swizzledSelector:@selector(zx_substringToIndex:)];
    
    ///substringWithRange:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(substringFromIndex:) swizzledSelector:@selector(zx_substringFromIndex:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(substringWithRange:) swizzledSelector:@selector(zx_substringWithRange:)];

    ///rangeOfString:options:range:locale:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(rangeOfString:options:range:locale:) swizzledSelector:@selector(zx_rangeOfString:options:range:locale:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(rangeOfString:options:range:locale:) swizzledSelector:@selector(zx_rangeOfString:options:range:locale:)];
    
    ///rangeOfCharacterFromSet:options:range:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFConstantString originalSelector:@selector(rangeOfCharacterFromSet:options:range:) swizzledSelector:@selector(zx_rangeOfCharacterFromSet:options:range:)];
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(rangeOfCharacterFromSet:options:range:) swizzledSelector:@selector(zx_rangeOfCharacterFromSet:options:range:)];
    
    
    ///rangeOfComposedCharacterSequenceAtIndex:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(rangeOfComposedCharacterSequenceAtIndex:) swizzledSelector:@selector(zx_rangeOfComposedCharacterSequenceAtIndex:)];

    ///rangeOfComposedCharacterSequencesForRange:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(rangeOfComposedCharacterSequencesForRange:) swizzledSelector:@selector(zx_rangeOfComposedCharacterSequencesForRange:)];

    ///stringByAppendingString:
    [ZXAvoidCrashRuntime exchangeInstanceMethodWithClass:__NSCFString originalSelector:@selector(stringByAppendingString:) swizzledSelector:@selector(zx_stringByAppendingString:)];

    
    [NSMutableString zx_avoidCrashExchangeMethod];
}


#pragma mark - 初始化

#pragma mark - initWithUTF8String:

//=================================================================
/*
 initWithUTF8String:
*/
//=================================================================
//*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSPlaceholderString initWithUTF8String:]: NULL cString'
- (nullable instancetype)zx_initWithUTF8String:(const char *)nullTerminatedCString{
    id instance = nil;
    @try {
        instance = [self zx_initWithUTF8String:nullTerminatedCString];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return instance;
    }
}


#pragma mark - initWithString:

//=================================================================
/*
 initWithString:
 （其参数是字符串常量的时候创建等同于字面值创建,构造出来的是字符串常量）
*/
//=================================================================
/*
 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSPlaceholderString initWithString:]: nil argument'
 */
- (instancetype)zx_initWithString:(NSString *)aString{
    id instance = nil;
    @try {
        instance = [self zx_initWithString:aString];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return instance;
    }
}

#pragma mark - 方法

#pragma mark - - (unichar)characterAtIndex:(NSUInteger)index

- (unichar)zx_characterAtIndex:(NSUInteger)index{
    unichar unicode;
    @try {
        unicode = [self zx_characterAtIndex:index];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return unicode;
    }
}

#pragma mark - 获取子字符串

// （1）从from索引开始且包括from：【>=from】
- (NSString *)zx_substringFromIndex:(NSUInteger)from{
    NSString *string;
    @try {
        string = [self zx_substringFromIndex:from];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return string;
    }
}

// （2）到to索引但不包括to：【<to】
- (NSString *)zx_substringToIndex:(NSUInteger)to{
    NSString *string;
    @try {
        string = [self zx_substringToIndex:to];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return string;
    }
}

// （3）根据range获取指定字符串
- (NSString *)zx_substringWithRange:(NSRange)range{
    NSString *string;
    @try {
        string = [self zx_substringWithRange:range];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return string;
    }
}

#pragma mark - rangeOfString:options:range:locale:
//=========================================================
//rangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch locale:(nullable NSLocale *)locale
//=========================================================

///1.*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFString rangeOfString:options:range:locale:]: nil argument'
///2.*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSCFString rangeOfString:options:range:locale:]: Range {0, 15} out of bounds; string length 12'


- (NSRange)zx_rangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch locale:(nullable NSLocale *)locale{
    NSRange range = {NSNotFound, 0};
    @try {
        range = [self zx_rangeOfString:searchString options:mask range:rangeOfReceiverToSearch locale:locale];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return range;
    }
}

#pragma mark - rangeOfCharacterFromSet:options:range:
//=========================================================
//- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)searchSet options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch
//=========================================================

- (NSRange)zx_rangeOfCharacterFromSet:(NSCharacterSet *)searchSet options:(NSStringCompareOptions)mask range:(NSRange)rangeOfReceiverToSearch
{
    NSRange range = {NSNotFound, 0};
    @try {
        range = [self zx_rangeOfCharacterFromSet:searchSet options:mask range:rangeOfReceiverToSearch];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return range;
    }
}


- (NSRange)zx_rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)index{
    NSRange range = {NSNotFound, 0};
    @try {
        range = [self zx_rangeOfComposedCharacterSequenceAtIndex:index];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return range;
    }
}
- (NSRange)zx_rangeOfComposedCharacterSequencesForRange:(NSRange)range{
    NSRange range1 = {NSNotFound, 0};
    @try {
        range1 = [self zx_rangeOfComposedCharacterSequencesForRange:range];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return range1;
    }
}


#pragma mark - stringByAppendingString:
//=========================================================
//- (NSString *)stringByAppendingString:(NSString *)aString;
//=========================================================
///*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSCFString stringByAppendingString:]: nil argument'
- (NSString *)zx_stringByAppendingString:(NSString *)aString
{
    NSString *str = self;
    @try {
        str = [self zx_stringByAppendingString:aString];
    } @catch (NSException *exception) {
        [ZXAvoidCrashReport crashReportWithException:exception];
    } @finally {
        return str;
    }
}


@end
